package org.bees.cms.core.clause;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Transient;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.bees.cms.common.Enumerable;
import org.bees.cms.common.Transformable;
import org.bees.cms.core.Specific;

@Entity
public class Criterion extends Clause implements Transformable<JSONObject>, Specific<String> {
	private static final long serialVersionUID = -3342117952368385271L;

	private Connector connector;
	private List<Criterion> criterions = new ArrayList<Criterion>();
	private List<Rule> rules = new ArrayList<Rule>();

	@Override
	public void from(JSONObject t) throws Exception {
		connector = Connector.valueOf(t.getString("op").toUpperCase());

		criterions.clear();
		JSONArray _criterions = t.containsKey("groups") ? t.getJSONArray("groups") : new JSONArray();
		for (int i = 0; i < _criterions.size(); i++) {
			Criterion criterion = new Criterion();
			criterion.from(_criterions.getJSONObject(i));
			criterions.add(criterion);
		}

		rules.clear();
		JSONArray _rules = t.containsKey("rules") ? t.getJSONArray("rules") : new JSONArray();
		for (int i = 0; i < _rules.size(); i++) {
			Rule rule = new Rule();
			rule.from(_rules.getJSONObject(i));
			rules.add(rule);
		}
	}

	@Override
	public JSONObject to() throws Exception {
		JSONObject object = new JSONObject();

		object.accumulate("op", connector.name());

		JSONArray _criterions = new JSONArray();
		for (Criterion criterion : criterions) {
			_criterions.add(criterion.to());
		}
		object.accumulate("groups", _criterions);

		JSONArray _rules = new JSONArray();
		for (Rule rule : rules) {
			_rules.add(rule.to());
		}
		object.accumulate("rules", _rules);

		return object;
	}

	@Override
	public String toSQL(Adapter adapter, Class<?> mapper, String alias) throws Exception {
		String result = "";

		for (Criterion criterion : criterions) {
			result = connector.connect(result, "(" + criterion.toSQL(adapter, mapper, alias) + ")");
		}

		for (Rule rule : rules) {
			result = connector.connect(result, rule.toSQL(adapter, mapper, alias));
		}

		return result;
	}

	public static enum Connector implements Enumerable {
		AND("并且") {
			@Override
			public String connect(String left, String right) throws Exception {
				StringBuilder builder = new StringBuilder();
				if (left != null && !left.trim().equals("")) {
					builder.append(left);
				}
				if (left != null && !left.trim().equals("") && right != null && !right.trim().equals("")) {
					builder.append(" AND ");
				}
				if (right != null && !right.trim().equals("")) {
					builder.append(right);
				}
				return builder.toString();
			}
		},
		OR("或者") {
			@Override
			public String connect(String left, String right) throws Exception {
				StringBuilder builder = new StringBuilder();
				if (left != null && !left.trim().equals("")) {
					builder.append(left);
				}
				if (left != null && !left.trim().equals("") && right != null && !right.trim().equals("")) {
					builder.append(" OR ");
				}
				if (right != null && !right.trim().equals("")) {
					builder.append(right);
				}
				return builder.toString();
			}
		};

		private String key;

		private Connector(String key) {
			this.key = key;
		}

		public abstract String connect(String left, String right) throws Exception;

		@Override
		public String key() {
			return key;
		}

		@Override
		public String value() {
			return name();
		}

	}

	public static class Rule implements Transformable<JSONObject>, Generator {
		private Operator operator;
		private String field;
		private String value;
		private Type type;

		@Override
		public void from(JSONObject t) throws Exception {
			operator = Operator.valueOf(t.getString("op").toUpperCase());
			field = t.getString("field");
			value = t.getString("value");
			type = Type.valueOf(t.getString("type").toUpperCase());
		}

		@Override
		public JSONObject to() throws Exception {
			JSONObject object = new JSONObject();
			object.accumulate("op", operator.name());
			object.accumulate("field", field);
			object.accumulate("value", value);
			object.accumulate("type", type.name());
			return object;
		}

		@Override
		public String toSQL(Adapter adapter, Class<?> mapper, String alias) throws Exception {
			return operator.operate(adapter.adapt(mapper, field, alias), value, type);
		}

		public static enum Operator implements Enumerable {
			EQUAL("相等于") {
				@Override
				public String operate(String name, String value, Type type) {
					return name + " = " + type.parse(value);
				}
			},
			NOTEQUAL("不等于") {
				@Override
				public String operate(String name, String value, Type type) {
					return name + " != " + type.parse(value);
				}
			},
			STARTWITH("以...开始") {
				@Override
				public String operate(String name, String value, Type type) {
					return name + " LIKE " + type.parse(value + "%");
				}
			},
			ENDWITH("以...结束") {
				@Override
				public String operate(String name, String value, Type type) {
					return name + " LIKE " + type.parse("%" + value);
				}
			},
			LIKE("以...相似") {
				@Override
				public String operate(String name, String value, Type type) {
					return name + " LIKE " + type.parse("%" + value + "%");
				}
			},
			GREATER("大于") {
				@Override
				public String operate(String name, String value, Type type) {
					return name + " > " + type.parse(value);
				}
			},
			GREATEROREQUAL("大于或等于") {
				@Override
				public String operate(String name, String value, Type type) {
					return name + " >= " + type.parse(value);
				}
			},
			LESS("小于") {
				@Override
				public String operate(String name, String value, Type type) {
					return name + " < " + type.parse(value);
				}
			},
			LESSOREQUAL("小于或等于") {
				@Override
				public String operate(String name, String value, Type type) {
					return name + " <= " + type.parse(value);
				}
			},
			IN("在...之内") {
				@Override
				public String operate(String name, String value, Type type) {
					String[] array = value.split(",");
					StringBuilder builder = new StringBuilder();
					for (String temp : array) {
						builder.append(type.parse(temp)).append(",");
					}
					value = builder.substring(0, builder.length() - (builder.toString().endsWith(",") ? 1 : 0));
					return name + " IN " + "(" + value + ")";
				}
			},
			NOTIN("不在...之内") {
				@Override
				public String operate(String name, String value, Type type) {
					String[] array = value.split(",");
					StringBuilder builder = new StringBuilder();
					for (String temp : array) {
						builder.append(type.parse(temp)).append(",");
					}
					value = builder.substring(0, builder.length() - (builder.toString().endsWith(",") ? 1 : 0));
					return name + " NOT IN " + "(" + value + ")";
				}
			};

			private String key;

			private Operator(String key) {
				this.key = key;
			}

			public abstract String operate(String name, String value, Type type);

			@Override
			public String key() {
				return key;
			}

			@Override
			public String value() {
				return name();
			}

		}

		public static enum Type implements Enumerable {
			STRING("字符串") {
				@Override
				public String parse(String value) {
					return "'" + value + "'";
				}
			},
			TEXT("文本") {
				@Override
				public String parse(String value) {
					return "'" + value + "'";
				}
			},
			NUMBER("数字") {
				@Override
				public String parse(String value) {
					return value;
				}
			},
			INT("整型") {
				@Override
				public String parse(String value) {
					return value;
				}
			},
			FLOAT("浮点型") {
				@Override
				public String parse(String value) {
					return value;
				}
			},
			DATE("日期") {
				@Override
				public String parse(String value) {
					return "'" + value + "'";
				}
			};

			private String key;

			private Type(String key) {
				this.key = key;
			}

			public abstract String parse(String value);

			@Override
			public String key() {
				return key;
			}

			@Override
			public String value() {
				return name();
			}

		}

	}

	@Transient
	@Column(name = "particular")
	public Connector getConnector() {
		return connector;
	}

	public void setConnector(Connector connector) {
		this.connector = connector;
	}

	@Transient
	@Column(name = "particular")
	public List<Criterion> getCriterions() {
		return criterions;
	}

	public void setCriterions(List<Criterion> criterions) {
		this.criterions = criterions;
	}

	@Transient
	@Column(name = "particular")
	public List<Rule> getRules() {
		return rules;
	}

	public void setRules(List<Rule> rules) {
		this.rules = rules;
	}

	@Column(length = 1000)
	@Override
	public String getParticular() throws Exception {
		return to().toString();
	}

	@Override
	public void setParticular(String particular) throws Exception {
		from(JSONObject.fromObject(particular));
	}

}
